﻿using System.Collections.Concurrent;

namespace MoonFramework;

/// <summary>
///  泛型对象池管理器
///  服务端对象池，线程安全
/// </summary>
public class ObjPoolManager : BaseManager<ObjPoolManager>
{
    private readonly ConcurrentDictionary<string, PoolNode> pool = new();
    private readonly SemaphoreSlim preloadSemaphore = new(1, 1); // 用于控制异步预加载的并发

    /// <summary>
    /// 缓存池节点
    /// </summary>
    protected class PoolNode
    {
        public readonly LinkedList<object> poolLinkedList;
        private readonly object lockObj = new(); // 用于保护LinkedList的访问

        public PoolNode()
        {
            poolLinkedList = new LinkedList<object>();
        }

        public void Push(object obj)
        {
            lock (lockObj)
            {
                poolLinkedList.AddLast(obj);
            }
        }

        public object PopTop()
        {
            lock (lockObj)
            {
                var obj = poolLinkedList.First;
                if (obj != null)
                {
                    poolLinkedList.RemoveFirst();
                    return obj.Value;
                }

                return null;
            }
        }

        public object PopLast()
        {
            lock (lockObj)
            {
                var obj = poolLinkedList.Last;
                if (obj != null)
                {
                    poolLinkedList.RemoveLast();
                    return obj.Value;
                }

                return null;
            }
        }

        public int Count
        {
            get
            {
                lock (lockObj)
                {
                    return poolLinkedList.Count;
                }
            }
        }
    }

    /// <summary>
    ///     从对象池获取对象
    /// </summary>
    /// <param name="type">对象池类型</param>
    /// <param name="objectFactory">对象创建工厂</param>
    /// <param name="callback">获取对象后的回调</param>
    public async Task<T?> PopAsync<T>(string type, Func<Task<T>> objectFactory = null, Action<T?> callback = null)
        where T : class
    {
        T? obj;
        if (pool.TryGetValue(type, out var poolNode) && poolNode.Count > 0)
            obj = poolNode.PopTop() as T;
        else
            obj = await objectFactory();
        callback?.Invoke(obj);
        return obj;
    }

    public T? Pop<T>(string type, Func<T> objectFactory, Action<T?> callback = null) where T : class
    {
        T? obj;
        if (pool.TryGetValue(type, out var poolNode) && poolNode.Count > 0)
        {
            obj = poolNode.PopTop() as T;
            callback?.Invoke(obj);
        }
        else
        {
            obj = objectFactory();
            callback?.Invoke(obj);
        }

        return obj;
    }

    /// <summary>
    ///     将对象归还到池中
    /// </summary>
    /// <param name="type">对象池类型</param>
    /// <param name="obj">要归还的对象</param>
    public void Push<T>(string type, T obj) where T : class
    {
        var poolNode = pool.GetOrAdd(type, _ => new PoolNode());
        poolNode.Push(obj);
    }

    /// <summary>
    ///     预加载对象池
    /// </summary>
    /// <param name="type">对象池类型</param>
    /// <param name="count">预加载数量</param>
    /// <param name="objectFactory">对象创建工厂</param>
    public async Task PreloadAsync<T>(string type, int count, Func<Task<T>> objectFactory)
    {
        await preloadSemaphore.WaitAsync();

        try
        {
            var poolNode = pool.GetOrAdd(type, _ => new PoolNode());

            // 异步预加载指定数量的对象
            for (var i = 0; i < count; i++)
            {
                var obj = await objectFactory();
                poolNode.Push(obj);
            }
        }
        finally
        {
            preloadSemaphore.Release();
        }
    }

    /// <summary>
    ///     清空对象池
    /// </summary>
    public void Clear()
    {
        foreach (var type in pool.Keys)
            RemoveType(type);
        pool.Clear();
    }

    /// <summary>
    ///     移除指定类型的对象池
    /// </summary>
    public void RemoveType(string type)
    {
        if (pool.TryGetValue(type, out var poolNode))
        {
            poolNode.poolLinkedList.Clear();
            pool.TryRemove(type, out _);
        }
    }
}